스마트 포인터를 이용해서 동적 할당에 대한 메모리 해제 관리를 수월하게 할 수 있다.
하지만, 스마트 포인터를 기본 포인터와 달리 암시적 변환(implicit conversion)을 지원하지 않는다.
class Top{};
class Middle: public Top{};
class Bottom: public Middle{};
Top* pt1=new Middle;
Top* pt2=new Bottom;
const Top* pct2=pt1;
스마트 포인터(smartPtr)스마트 포인터(템플릿 클래스)에서 위처럼 포인터를 변환해서 사용하고 싶은 경우,
template <typename T>
class SmartPtr{
public:
explicit SmartPtr(T* realPtr);
};
SmartPtr<Top> pt1=SmartPtr<Middle>(new Middle);
SmartPtr<Top> pt2=SmartPtr<Buttom>(new Buttom);
SmartPtr<const Top> pct2=pt1;
컴파일러에게는 SmartPtr<Middle>과 SmartPtr<Top>이 완전히 별개의 클래스로 인식된다.
위처럼 SmartPtr 템플릿 클래스 사이에서 암시적 변환을 하고 싶은 경우,
변환이 되도록 SmartPtr을 코딩해 주어야 한다.(각 변환에 대한 생성자 함수를 직접 만들어 주어야 한다.)
새로운 클래스가 추가될 경우, 그에 대한 코딩을 또 수정해야 함
위와 같이 생성자를 모두 직접 만드는 것은 매우 반복적인 작업이다.
템플릿을 인스턴스화 하면 새로운 함수를 생성할 수 있는 방법을 사용해,
생성자를 만들어 내는 템플릿(template)을 사용할 수 있다.
template <typename T>
class SmartPtr{
public:
template <typename U>
SmartPtr(const SmartPtr<U>& other);
}
위의 템플릿 복사생성자(일반화된 복사 생성자)를 explicit으로 정의하지 않았기 떄문에,
기본 타입에 대해 제공하는 포인터 타입 사이의 타입 캐스팅을 그대로 사용할 수 있다.
SmartPtr<Bottom> -> SmartPtr<Top>은 제공하지만,
SmartPtr<Top> -> SmartPtr<Buttom>은 제공하지 않기 위함
하지만, 위의 구현은 double* -> int* 변환까지도 제공함
SmartPtr도 get 멤버함수를 통해 객체의 기본제공 포인터의 사본을 반환하도록 구현
(생성자 템플릿에 원하는 타입 변환 제약을 가할 수 있음)
template <typename T>
class SmartPtr{
public:
template <typename U>
SmartPtr(const SmartPtr<U>& other): heldPtr(other.get()){}
T* get() const{
return heldPtr;
}
private:
T* heldPtr;
};
위처럼 구현하면, U* 에서 T*로 진행되는 암시적 변환이 허용될 때만, 컴파일 에러가 발생하지 않는다.
(기본 타입 포인터에 대하여, 허용되지 않은 암시적 변환 수행시, 컴파일 에러)
shared_ptr
template <class T> class shared_ptr{
public:
template <class Y>
explicit shared_ptr(Y* p);
template <class Y>
shared_ptr(shared_ptr<Y> const& r);
template <class Y>
explicit shared_ptr(weak_ptr<Y> const& r);
template <class Y>
explicit shared_ptr(auto_ptr<Y>& r);
template <class Y>
shared_ptr& operator=(shared_ptr<Y> const& r);
template <class Y>
shared_ptr& operator=(auto_ptr<Y> const& r);
};
위와 같이 shared_ptr은 shared_ptr, weak_ptr, auto_ptr 객체를 통해 생성자 호출을 제공한다.
(대입 연산자는 weak_ptr에 대해서 제공하지 않음)
shared_ptr<Y>를 인자로 하는 일반화 복사 생성자를 제외하고 모든 생성자는 explicit으로 선언되어 있다.
(shared_ptr로 만든 다른 타입으로 진행되는 암시적 변환은 허용하지만, 기본제공 포인터 혹은,
다른 스마트 포인터 타입으로부터 변환되는 것은 금지함, 명시적으로 캐스팅해서 사용은 할 수 있다)
C++에서 복사 생성자와 일반화 복사 생성자(template 복사 생성자)는 서로 다르다.
(일반화 복사 생성자만 선언해 주어도, 컴파일러는 복사 생성자가 없다고 생각하고 복사 생성자를 default로 선언한다.)
template <class T> class shared_ptr{
public:
shared_ptr(shared_ptr const& r);
template <class Y>
shared_ptr(shared_ptr<Y> const& r);
shared_ptr& operator=(shared_ptr const& r);
template <class Y>
shared_ptr& operator=(shared_ptr<Y> const& r);
};